package com.guisu.material.recyclerview.custom

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import com.guisu.material.R
import kotlin.math.abs
import kotlin.math.max

class RecyclerView: ViewGroup {

    /**
     * 适配器
     */
    private var mAdapter: Adapter? = null

    /**
     * 回收池
     */
    private var mRecycler: Recycler? = null

    /**
     * 是否需要重新摆放
     */
    private var mNeedRelayout = false

    /**
     * 缓存可见View集合
     */
    private val mViewList by lazy { arrayListOf<View>() }

    /**
     * adapter的条数
     */
    private var mRowCount = 0

    /**
     * 每一行的高度
     */
    private lateinit var heights: IntArray

    /**
     * RecyclerView的宽度
     */
    private var width = 0

    /**
     * RecyclerView的高度
     */
    private var height = 0

    /**
     * 点击事件最大像素范围
     */
    private var touchSlop = 0

    /**
     * 当前滑动的Y值
     */
    private var currentY = 0

    /**
     * 滑动偏移
     */
    private var scrollY = 0

    /**
     * 第一个可见Item在数据数组的索引
     */
    private var firstRow = 0

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(
        context,
        attrs,
        defStyleAttr,
        0
    )

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
        initData(context, attrs)
    }

    private fun initData(context: Context, attrs: AttributeSet?) {
        // 初始化自定义属性
        mNeedRelayout = true
        val viewConfiguration = ViewConfiguration.get(context)
        touchSlop = viewConfiguration.scaledTouchSlop
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    // onLayout会被不断调用
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        // changed 屏幕转换的时候会改变
        if (mNeedRelayout || changed) {
            mNeedRelayout = false

            // 摆放的时候，初始化
            mViewList.clear()
            // 比较耗时间
            removeAllViews()
            val adapter = mAdapter ?: throw RuntimeException("请配置Adapter")
            val rowCount = adapter.getItemCount()
            // 高度
            heights = IntArray(rowCount)
            for (i in 0 ..< rowCount) {
                // 依赖这个方法测量item的高度
                heights[i] = adapter.getHeight(i)
            }
            width = r - l
            height = b - t
            var top = 0
            var bottom = 0
            var i = 0
            while (i < rowCount && top < height) {
                bottom = top + heights[i]
                // 实例化 布局
                // 怎么摆放
                // 摆放多少个
                val view = makeAndSetup(i, 0, top, width, bottom)
                mViewList.add(view)
                top = bottom
                i++
            }
        }
    }

    private fun makeAndSetup(index: Int, left: Int, top: Int, right: Int, bottom: Int): View {
        val view = obtain(index, right - left, bottom - top)
        view.layout(left, top, right, bottom)
        return view
    }

    private fun obtain(row: Int, width: Int, height: Int): View {
        // 回收池
        val adapter = mAdapter ?: throw RuntimeException("请配置适配器")
        val type = adapter.getItemViewType(row)
        val recyclerView = mRecycler?.getRecyclerView(type)
        val view = if (recyclerView == null) {
            adapter.onCreateViewHolder(row, null, this)
        } else {
            // 刷新数据
            adapter.onBindViewHolder(row, recyclerView, this)
        }
        // tag值 填充 移除
        view.setTag(R.id.tag_type_view, type)
        // 测量
        view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
        addView(view, 0)
        return view
    }

    override fun removeView(view: View?) {
        super.removeView(view)
        if (view == null) return
        val type = view.getTag(R.id.tag_type_view) as Int
        mRecycler?.addRecyclerView(view, type)
    }

    fun setAdapter(adapter: Adapter) {
        mNeedRelayout = true
        this.mAdapter = adapter
        mRecycler = Recycler(adapter.getViewTypeCount())
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        var intercept = false
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                currentY = ev.rawY.toInt()
            }
            MotionEvent.ACTION_MOVE -> {
                val y2 = abs(currentY - ev.rawY).toInt()
                if (y2 > touchSlop) {
                    intercept = true
                }
            }
        }
        return intercept
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action) {
            MotionEvent.ACTION_MOVE -> {
                val y2 = event.rawY.toInt()
                val diff = currentY - y2
                scrollBy(0, diff)
            }
        }

        return super.onTouchEvent(event)
    }

    override fun scrollBy(x: Int, diff: Int) {
        scrollY += diff

        // 修正
        scrollY = scrollBounds(scrollY, firstRow, heights, height)
        if (scrollY > 0) {
            while (heights[firstRow] < scrollY) {
                if (mViewList.isNotEmpty()) {
                    // 移除最顶上的View
                    removeView(mViewList.removeAt(0))
                }
                scrollY -= heights[firstRow]
                firstRow++
            }
            while (getFilledHeight() < height ) {
                // 添加View
                val size = mViewList.size
                val dataIndex = firstRow + size
                val view = obtain(dataIndex, width, heights[dataIndex])
                mViewList.add(view)
            }
        } else {
            // 往下滑 下滑的距离超过一个Item的高度
            while (mViewList.isNotEmpty() && getFilledHeight() - heights[firstRow + mViewList.size -1] >= 0) {
                val view = mViewList.removeAt(mViewList.size - 1)
                removeView(view)
            }
            while (0 > scrollY) {
                firstRow--
                val view = obtain(firstRow, width, heights[firstRow])
                mViewList.add(0, view)
                scrollY += heights[firstRow + 1]
            }
        }
        repositionViews()
    }

    private fun scrollBounds(scrollY: Int, firstRow: Int, size: IntArray, viewSize: Int): Int {
        var scY = scrollY
        if (scY == 0) {

        } else if (scY < 0) {
            //修整下滑的临界值
            scY = max(scY, -sumArray(size, 0, firstRow))
        } else {
            scY = max(scY, max(0, sumArray(size, firstRow, size.size)))
        }
        return scrollY
    }

    private fun getFilledHeight(): Int {
        return sumArray(heights, firstRow, mViewList.size) - scrollY
    }

    private fun sumArray(array: IntArray, firstIndex: Int, count: Int): Int {
        var sum = 0
        var c = count
        c += firstIndex
        for (i in firstIndex..< count) {
            sum += array[i]
        }
        return sum
    }

    private fun repositionViews() {
        var left = 0
        var top = 0
        var right = 0
        var bottom = 0
        var i = 0
        top = - scrollY
        for (view in mViewList) {
            bottom = top + heights[i++]
            view.layout(0, top, width, bottom)
            top = bottom
        }
    }

    interface Adapter {
        fun onCreateViewHolder(position: Int, convertView: View?, parent: ViewGroup?): View
        fun onBindViewHolder(position: Int, convertView: View, parent: ViewGroup?): View
        fun getItemCount(): Int
        fun getItemViewType(row: Int): Int
        fun getViewTypeCount(): Int
        fun getHeight(index: Int): Int
    }
}